/*!
\copyright  Copyright (c) 2019-2023 Qualcomm Technologies International, Ltd.
            All Rights Reserved.
            Qualcomm Technologies International, Ltd. Confidential and Proprietary.
\file
\brief      The audio interface implementation for hfp voice sources
*/

#include "hfp_profile_audio_params.h"
#include "hfp_profile_audio_if/hfp_profile_audio_if.h"
#include "hfp_profile_audio.h"
#include "hfp_profile_instance.h"
#include "hfp_profile_port.h"
#include "hfp_profile_sm.h"
#include "hfp_abstraction.h"
#include "mirror_profile.h"
#include "kymera_adaptation.h"

#include "source_param_types.h"
#include "state_proxy.h" // this shouldn't be here
#include "voice_sources.h"
#include "volume_system.h"

#include <stdlib.h>
#include <panic.h>

static bool hfpProfile_IsAudioRouted(voice_source_t source);
static bool hfpProfile_IsVoiceChannelAvailable(voice_source_t source);
static source_status_t hfpProfile_SetState(voice_source_t source, source_state_t state);
static source_state_t hfpProfile_GetState(voice_source_t source);

static const voice_source_audio_interface_t hfp_audio_interface =
{
    .GetConnectParameters = HfpProfile_GetConnectParameters,
    .ReleaseConnectParameters = HfpProfile_FreeConnectParameters,
    .GetDisconnectParameters = HfpProfile_GetDisconnectParameters,
    .ReleaseDisconnectParameters = HfpProfile_FreeDisconnectParameters,
    .IsAudioRouted = hfpProfile_IsAudioRouted,
    .IsVoiceChannelAvailable = hfpProfile_IsVoiceChannelAvailable,
    .SetState = hfpProfile_SetState,
    .GetState = hfpProfile_GetState
};

static bool hfpProfile_IsAudioRouted(voice_source_t source)
{
    bool is_routed = FALSE;

    hfpInstanceTaskData * instance = HfpProfileInstance_GetInstanceForSource(source);

    if(instance && (instance->source_state == source_state_connected))
    {
        is_routed = TRUE;
    }

    DEBUG_LOG_VERBOSE("hfpProfile_IsAudioRouted source enum:voice_source_t:%d, routed=%d", source, is_routed);

    return is_routed;
}

static bool hfpProfile_IsVoiceChannelAvailable(voice_source_t source)
{
    bool is_available = FALSE;

    hfpInstanceTaskData * instance = HfpProfileInstance_GetInstanceForSource(source);

    if (instance && (HfpProfile_IsScoActiveForInstance(instance) || instance->bitfields.esco_connecting))
    {
        is_available = TRUE;
    }

    DEBUG_LOG_VERBOSE("hfpProfile_IsVoiceChannelAvailable source enum:voice_source_t:%d, available=%d", source, is_available);

    return is_available;
}

static source_status_t hfpProfile_SetState(voice_source_t source, source_state_t state)
{
    hfpInstanceTaskData* instance = HfpProfileInstance_GetInstanceForSource(source);

    if(instance)
    {
        source_state_t old_state = instance->source_state;
        instance->source_state = state;

        DEBUG_LOG_FN_ENTRY("hfpProfile_SetState(%p) source enum:voice_source_t:%d, state from enum:source_state_t:%d to enum:source_state_t:%d, hfp_state enum:hfpState:%d",
                           instance, source, old_state, state, appHfpGetState(instance));

        switch(state)
        {
            case source_state_connecting:
                /* Prevent routing SCO until the source is available or until it loses focus */
                if(HfpProfile_IsScoConnectingForInstance(instance) || !HfpProfile_IsScoActiveForInstance(instance))
                {
                    return source_status_preparing;
                }
                /* Restore scheduling priority of routed audio */
                hfpProfile_LowerScoPriority(instance, FALSE);
            break;
            
            case source_state_disconnected:
                /* Lower scheduling priority of unrouted audio */
                hfpProfile_LowerScoPriority(instance, TRUE);
            break;

            case source_state_disconnecting:
            /*Transfer the call to AG before audio router gets disconnected*/
                if(HfpProfile_IsScoActiveForInstance(instance))
                {
                    return source_status_preparing;
                }
            break;

            default:
            break;
        }
    }
    else
    {
        DEBUG_LOG_INFO("hfpProfile_SetState no hfp instance found for source  enum:voice_source_t:%d", source);
    }

    return source_status_ready;
}

static source_state_t hfpProfile_GetState(voice_source_t source)
{
    source_state_t state = source_state_invalid;
    hfpInstanceTaskData* instance = HfpProfileInstance_GetInstanceForSource(source);

    if(instance)
    {
        state = instance->source_state;

        DEBUG_LOG_FN_ENTRY("hfpProfileGetState(%p) source enum:voice_source_t:%d, state enum:source_state_t:%d, hfp_state enum:hfpState:%d",
                           instance, source, state, appHfpGetState(instance));
    }
    else
    {
        DEBUG_LOG_INFO("hfpProfileGetState no hfp instance found for source  enum:voice_source_t:%d", source);
    }

    return state;
}

const voice_source_audio_interface_t * HfpProfile_GetAudioInterface(void)
{
    return &hfp_audio_interface;
}

void HfpProfile_StoreConnectParams(hfpInstanceTaskData * instance, uint8 codec, uint8 wesco, uint8 tesco, uint16 qce_codec_mode_id)
{
    PanicNull(instance);

    instance->codec = codec;
    instance->wesco = wesco;
    instance->tesco = tesco;

#ifdef INCLUDE_SWB
    instance->qce_codec_mode_id = qce_codec_mode_id;
#else
    UNUSED(qce_codec_mode_id);
#endif
}
